/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2012/11/19
* File: EnnSceneNode.h
* Blog: http://www.cnblogs.com/zengqh/
**/

#ifndef EnnSceneNode_h__
#define EnnSceneNode_h__

#include "EnnPrerequisites.h"
#include "EnnAxisAlignedBox.h"

namespace Enn
{
	class SceneNode : public SceneNodeAlloc
	{
	public:
		enum TransformSpace
		{
			/// Transform is relative to the local space
			TS_LOCAL,
			/// Transform is relative to the space of the parent node
			TS_PARENT,
			/// Transform is relative to world space
			TS_WORLD
		};

		typedef map<String, MovableObject*>::type ObjectMap;
		typedef ObjectMap::iterator ObjectIterator;
		typedef ObjectMap::const_iterator ObjectConstIterator;

		typedef map<String, SceneNode*>::type ChildMap;
		typedef ChildMap::iterator ChildIterator;
		typedef ChildMap::iterator ChildConstIterator;

	public:
		class Listener
		{
		public:
			Listener();
			virtual ~Listener();

			/** Called when a node gets updated.
			@remarks
				Note that this happens when the node's derived update happens,
				not every time a method altering it's state occurs. There may 
				be several state-changing calls but only one of these calls, 
				when the node graph is fully updated.
			*/
			virtual void SceneNodeUpdated(SceneNode*)
			{

			}

			/** Node is being destroyed */
			virtual void SceneNodeDestroyed(SceneNode*)
			{

			}

			/** Node has been attached to a parent */
			virtual void SceneNodeAttached(SceneNode*)
			{

			}

			/** Node has been detached from a parent */
			virtual void SceneNodeDetached(SceneNode*)
			{

			}
		};

	public:
		SceneNode(const String& name, SceneManager* creator);
		virtual ~SceneNode();

		virtual const String& getName() const
		{
			return _name;
		}

		/** Gets this node's parent (NULL if this is the root).
        */
		virtual SceneNode* getParent() const;

		virtual void setParent(SceneNode* parent);

		/** Returns a quaternion representing the nodes orientation.
        */
		virtual const quat& getOrientation() const;

		/** Sets the orientation of this node via a quaternion.
        @remarks
            Orientations, unlike other transforms, are not always inherited by child nodes.
            Whether or not orientations affect the orientation of the child nodes depends on
            the setInheritOrientation option of the child. In some cases you want a orientating
            of a parent node to apply to a child node (e.g. where the child node is a part of
            the same object, so you want it to be the same relative orientation based on the
            parent's orientation), but not in other cases (e.g. where the child node is just
            for positioning another object, you want it to maintain it's own orientation).
            The default is to inherit as with other transforms.
        @par
            Note that rotations are oriented around the node's origin.
        */
        virtual void setOrientation( const quat& q );

		virtual const vec3& getPosition() const;

		virtual void setPosition(const vec3& pos);

		virtual const vec3& getScale() const;

		virtual void setScale(const vec3& scale);

		virtual void scale(const vec3& scale);

		virtual void translate(const vec3& trans, TransformSpace relativeTo = TS_PARENT);

		virtual void roll(const Radian& angle, TransformSpace relativeTo = TS_LOCAL);

		virtual void pitch(const Radian& angle, TransformSpace relativeTo = TS_LOCAL);

		virtual void yaw(const Radian& angle, TransformSpace relativeTo = TS_LOCAL);

		/** Rotate the node around an arbitrary axis.
        */
        virtual void rotate(const vec3& axis, const Radian& angle, TransformSpace relativeTo = TS_LOCAL);

		/** Rotate the node around an aritrary axis using a Quarternion.
        */
        virtual void rotate(const quat& q, TransformSpace relativeTo = TS_LOCAL);

		virtual SceneNode* createChild(
			const String& name,
			const vec3& trans = vec3::ZERO,
			const quat& rot = quat::IDENTITY);

		virtual SceneNode* createChildImpl(const String& name);

		virtual void addChild(SceneNode* child);

		virtual uint16 numChildren() const;

		virtual SceneNode* getChild(const String& name) const;

		virtual SceneNode* removeChild(const String& name);

		virtual SceneNode* removeChild(SceneNode* scene_node);

		virtual void removeAllChild();

		virtual void updateFromParent();

		virtual void updateFromParentImpl();

		virtual const quat& getWorldOrientation();

		virtual const vec3& getWorldScale();

		virtual const vec3& getWorldPosition();

		virtual const mat4& getWorldTransform();

		virtual void setListener(Listener* listener);

		virtual Listener* getListener() const;

		virtual void setParentTransformCacheUpdated(bool dirty = true);

		virtual void updateTransform();

	protected:
		virtual void setSelfTransformCacheDirty(bool dirty = true);
		virtual void setChildTransformCacheDirty(bool dirty = true); 
		virtual bool isSelfTransformCacheDirty() const;
		virtual bool isChildTransformCacheDirty() const;
		virtual bool isParentTransformCacheDirty() const;

		virtual void transformCacheDirty(bool dirty = true);

	protected:
		String			_name;
		ObjectMap		_object_list;
		ChildMap		_child_list;
		SceneManager*	_creator;
		AxisAlignedBox	_local_aabb;
		SceneNode*		_parent;
		quat			_orientation;
		vec3			_position;
		vec3			_scale;
		Listener*		_listener;
		quat			_world_orientation;
		vec3			_world_scale;
		vec3			_world_position;
		mat4			_world_matrix;
		bool			_self_transform_cache_dirty;
		bool			_child_transform_cache_dirty;
		bool			_self_world_matrix_dirty;
		bool			_parent_transform_cache_dirty;
	};
}
#endif    /* EnnSceneNode_h__ */